/**
 * Copyright 2008 Mathias Doenitz, http://lis.to/
 *
 * This file is part of the lis.to java desktop client. The lis.to java desktop client is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * The lis.to java desktop client is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with the lis.to java desktop client.
 * If not, see http://www.gnu.org/licenses/
 */

package listo.client.model;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import com.thoughtworks.xstream.annotations.XStreamImplicit;
import listo.utils.types.Copyable;
import listo.utils.types.KeyValuePair;
import org.apache.commons.lang.StringUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Represents a task folder with an optional list of sub folders.
 */
@XStreamAlias("folder")
public class Folder extends ModelObject implements Copyable<Folder>, Cloneable {

    @SuppressWarnings("unchecked")
    private static final List<KeyValuePair<String, String>> ICONS = ImmutableList.of(
            new KeyValuePair<String, String>("yellow", "folder_yellow.png"),
            new KeyValuePair<String, String>("red", "folder_red.png"),
            new KeyValuePair<String, String>("blue", "folder_blue.png"),
            new KeyValuePair<String, String>("green", "folder_green.png"),
            new KeyValuePair<String, String>("orange", "folder_orange.png"),
            new KeyValuePair<String, String>("inbox", "inbox.png"),
            new KeyValuePair<String, String>("personal", "personal.png"),
            new KeyValuePair<String, String>("people", "people.png"),
            new KeyValuePair<String, String>("home", "home.png"),
            new KeyValuePair<String, String>("cart", "cart.png"),
            new KeyValuePair<String, String>("car", "car.png"),
            new KeyValuePair<String, String>("computer", "screen.png"),
            new KeyValuePair<String, String>("mail", "mail.png"),
            new KeyValuePair<String, String>("phone", "phone.png"),
            new KeyValuePair<String, String>("holiday", "umbrella.png"),
            new KeyValuePair<String, String>("hammer", "hammer.png"),
            new KeyValuePair<String, String>("heart", "heart.png"),
            new KeyValuePair<String, String>("music", "music.png"),
            new KeyValuePair<String, String>("clip", "clip.png"),
            new KeyValuePair<String, String>("pencil", "pencil.png"),
            new KeyValuePair<String, String>("steps", "steps.png"),
            new KeyValuePair<String, String>("chance", "chance.png"),
            new KeyValuePair<String, String>("gear", "gear.png"),
            new KeyValuePair<String, String>("world", "world.png"),
            new KeyValuePair<String, String>("flash", "flash.png"),
            new KeyValuePair<String, String>("important", "important.png"),
            new KeyValuePair<String, String>("pet", "pet.png"),
            new KeyValuePair<String, String>("star", "star.png"),
            new KeyValuePair<String, String>("sun", "sun.png"),
            new KeyValuePair<String, String>("sun_clouds", "sun_clouds.png"),
            new KeyValuePair<String, String>("clouds", "clouds.png"));

    private static final String DEFAULT_ICON = "folder";

    @XStreamAsAttribute private String name;
    @XStreamAsAttribute private String icon;

    @XStreamImplicit(itemFieldName = "folder") private List<Folder> folders;
    private transient Folder parent;

    public static String getIcon(String icon) {
        for (KeyValuePair<String, String> pair : ICONS) {
            if (pair.getKey().equals(icon)) return pair.getValue();
        }
        return "folder_yellow.png";
    }

    public static Object[] getIcons() {
        return Lists.transform(ICONS, new Function<KeyValuePair<String, String>, Object>() {
            public Object apply(KeyValuePair<String, String> from) {
                return from.getKey();
            }
        }).toArray();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getIcon() {
        return icon == null || getIcon(icon) == null ? DEFAULT_ICON : icon;
    }

    public void setIcon(String icon) {
        this.icon = DEFAULT_ICON.equals(icon) ? null : icon;
    }

    /**
     * @return the parent folder
     */
    public Folder getParent() {
        return parent;
    }

    /**
     * @return the path name of this Folder
     */
    public String getPathName() {
        if (parent == null) return null;
        String parentPathName = parent.getPathName();
        return parentPathName != null ? parentPathName + '/' + name : name;
    }

    /**
     * @return true if this folder has sub folders
     */
    public boolean hasFolders() {
        return folders != null;
    }

    /**
     * @return gets the list of sub folders or null
     */
    public List<Folder> getFolders() {
        return folders != null ? Collections.unmodifiableList(folders) : null;
    }

    /**
     * Adds the given folder as a sub folder to this folder.
     * The given folder must not currently have a parent.
     *
     * @param folder the sub folder
     */
    public void add(Folder folder) {
        add(folder, null);
    }

    /**
     * Adds the given folder as a sub folder to this folder.
     * The given folder must not currently have a parent.
     *
     * @param folder   the sub folder
     * @param position the optional position in the sub folder list
     */
    public void add(Folder folder, Integer position) {
        if (folders == null) {
            folders = new ArrayList<Folder>();
        }
        if (position != null) {
            folders.add(position, folder);
        } else {
            folders.add(folder);
        }

        folder.parent = this;
    }

    /**
     * Deletes the given folder from the sub folder list of this folder.
     *
     * @param folder the sub folder
     */
    public void delete(Folder folder) {
        if (!folders.remove(folder)) {
            throw new IllegalOperationException();
        }
        if (folders.size() == 0) folders = null;

        folder.parent = null;
    }

    /**
     * Moves this folder from the current parent to a new parent
     *
     * @param newParent the new parent folder
     * @param position  optional position in the new parents sub folder list.
     */
    public void moveTo(Folder newParent, Integer position) {
        parent.delete(this);
        if (newParent != null) {
            newParent.add(this, position);
        } else {
            parent.add(this, position);
        }
    }

    /**
     * Special method called after xstream deserialization to perform inititialization of transient fields.
     *
     * @return this
     */
    @SuppressWarnings({"UnusedDeclaration"})
    private Object readResolve() {
        if (folders != null) {
            for (Folder subFolder : folders) {
                subFolder.parent = this;
            }
        }
        return this;
    }

    /**
     * Returns a clone of this folder and its subtree.
     *
     * @return the cloned subtree
     */
    public Folder copy() {
        Folder clone;
        try {
            clone = (Folder) super.clone();
        }
        catch (CloneNotSupportedException e) {
            throw new IllegalStateException();
        }
        if (hasFolders()) {
            clone.folders = new ArrayList<Folder>(folders.size());
            for (Folder subFolder : folders) {
                clone.add(subFolder.copy());
            }
        }
        return clone;
    }

    public String toString() {
        return parent == null ? "ROOT" : getPathName();
    }

    public static Folder create(ObjectId id) {
        Folder folder = new Folder();
        folder.setId(id);
        return folder;
    }

    public static boolean isValidName(String text) {
        return StringUtils.isNotEmpty(text) && !StringUtils.containsAny(text, ",/ ");
    }

}
